package net.sk.ws;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.servlet.http.HttpServletRequest;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;
import org.eclipse.jetty.websocket.WebSocket.OnTextMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;


@Component
public class WSServlet extends WebSocketServlet {
	public static final String ACTION_SUBSCRIBE = "action.subscribe";

	protected Logger log = LoggerFactory.getLogger(this.getClass());
	private static final long serialVersionUID = 1L;
	private static HashMap<String,WSClient> clients=new HashMap<String,WSClient>();
	final AtomicInteger atomicInt = new AtomicInteger(0);
	final String PRE_ID = "ws_";
	private final ScriptEngine engine_out;
	ObjectMapper mapper;  
	
	public WSServlet() {
	    ScriptEngineManager manager = new ScriptEngineManager();
	    engine_out = manager.getEngineByName("js");
	    mapper=new ObjectMapper();
	}
	@Override
	public WebSocket doWebSocketConnect(HttpServletRequest req, String protocol) {
		// TODO Auto-generated method stub		
		WSClient w = new WSClient(PRE_ID+atomicInt.incrementAndGet());
		return w;
		
	}
	public void sendTo(String clientId,String msg){
		WSClient client = clients.get(clientId);
		client.sendMessage(msg);
	}
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void sendAction(String action){
    	StateDTO dto = new StateDTO();
    	dto.action=action;
    	String msg;
		try {
			msg = mapper.writeValueAsString(dto);
	    	for (String key : clients.keySet()) {
	    		WSClient client = clients.get(key);
	    		client.sendMessage(msg);
	    	}
		} catch (JsonGenerationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JsonMappingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
	}
	public synchronized void sendData(String action, List data){
		sendData(action,data.toArray());
	}
	public synchronized void sendData(String action, Object[] data){
		if(data==null || data.length==0){
			sendAction( action);
			return;
		}
		String cn = data[0].getClass().getName();
		for(int i=0; i<data.length; i++){
			Object obj = data[i];
			engine_out.put("e", obj);
	    	for (String key : clients.keySet()) {
	    		WSClient client = clients.get(key);
	    		if(!client.bSend(cn,engine_out)){
	                continue;
	            }
	    		client.sendAdd(obj );
	    	}
		}
    	for (String key : clients.keySet()) {
    		WSClient client = clients.get(key);
    		client.sendFlush(action, cn);
    	}
	}

	

	class WSClient implements OnTextMessage {
		WebSocket.Connection connection;
		//Gson gson;		
		ObjectMapper mapper;  
        
		//订阅的数据类
		private HashMap<String,String> smap=null;
		private String id;
		private List sendbuf;
		
		public String getId(){
			return id;
		}
		WSClient(String id){
			this.id = id;
			this.smap = new HashMap<String,String>();
			this.sendbuf = new ArrayList();
			mapper= new ObjectMapper();
		}

		/**
		 * 添加到发送缓冲
		 * @param obj
		 */
		public void sendAdd(Object obj){
			sendbuf.add(obj);
		}
		/**
		 * 将发送缓冲数据送出并清空缓冲
		 * @param action
		 * @param cn
		 */
		@SuppressWarnings({ "rawtypes", "unchecked" })
		public synchronized  void sendFlush(String action,String cn){
			if(sendbuf.isEmpty())
				return;
			StateDTO dto = new StateDTO();
			dto.action=action;
			dto.cn=cn;
			dto.tm=new Date();
			dto.data = this.sendbuf;
			try {
				sendMessage(mapper.writeValueAsString(dto));
			} catch (JsonGenerationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (JsonMappingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	
			sendbuf.clear();
		}
		/**
		 * 订阅指定条件的数据类
		 * @param sm
		 */
		@SuppressWarnings("unused")
		private void subscribe(List<ArrayList> sl){
			for(int i=0; i<sl.size(); i++){
				Object[] s=sl.get(i).toArray();
				log.debug(s[0].getClass().getName());
				smap.put(s[0].toString(), s[1].toString());
			}
		}
		/**
		 * 根据订阅条件 判定是否需要发送数据对象
		 * @param cn
		 * @param obj
		 * @return
		 * @throws ScriptException 
		 */
		public boolean bSend(String cn,ScriptEngine se){
			if(!smap.containsKey(cn)){
				return false;
			}else{
				String filter = smap.get(cn);
				if(filter==null || filter.trim().equals(""))
					return  true;
				else{
					try {
						log.info(se.get("e").toString());
						return (Boolean)se.eval(filter);
					} catch (ScriptException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
						return false;
					}
				}
			}
		}
		
		
	    @Override
	    public void onOpen(Connection connect) {
	        this.connection = connect;
			clients.put(getId(), this);
	        log.debug("open ws:"+this.getId());
	        //sendMessage("reload", loadDatas());
	    }
	    @Override
	    public void onClose(int code, String message) {
	        clients.remove(this.getId());
	        log.debug("close ws:"+this.getId());
	    }
	    @Override
	    public void onMessage(String message) {
	        //处理消息，通知到其他各个客户端
	    	log.debug(message);
	    	StateDTO<ArrayList> dto;
			try {
				dto = mapper.readValue(message, StateDTO.class);
		    	if(dto.action.endsWith(ACTION_SUBSCRIBE)){
		    		subscribe(dto.data);
		    	}
		    	sendData("action.event",HeartThread.randEvent1(100));
			} catch (JsonParseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (JsonMappingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		private void sendMessage(String message){
		    try {
		        this.connection.sendMessage(message);
		        //发送错误即可认为对方已退出连接
		    } catch (IOException e) {
		        e.printStackTrace();
		        this.connection.close();
		        clients.remove(this.getId());
		    }
		}

	}
	
	
}
